// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: xguo

#pragma once

#include <string>
#include <memory>

#include "coin2/exchange/base/order/order.h"
#include "coin2/exchange/base/symbology/product_info.h"
#include "coin2/exchange/factory/symbology/product.h"
#include "coin2/strategy/order_executor.pb.h"
#include "coin2/strategy/order_executor/cancel_executor.h"
#include "coin2/strategy/order_executor/working_order_state.h"

namespace coin2::strategy::order_executor {

class AggOrderExecutor {
 public:
  using IOrderSubsystem = exchange::base::order::IOrderSubsystem;
  using OrderQuerier = exchange::base::order::OrderQuerier;
  using IProduct = coin2::exchange::base::symbology::IProduct;
  using IProductHolder = coin2::exchange::base::symbology::IProductHolder;
  using IProductInfo = coin2::exchange::base::symbology::IProductInfo;
  using OrderDirection = coin::proto::OrderDirection;
  using OrderDuration = coin::proto::OrderDuration;
  using OrderExecutorSystemConfig = coin2::strategy::AggressiveExecutorConfig;
  using OrderSide = coin::proto::OrderSide;
  using OrderSpec = coin2::exchange::base::order::OrderSpec;
  using OrderType = coin::proto::OrderType;
  using GetProductHolderFunc = std::function<const IProductHolder*(const IProduct& product, int64_t timestamp)>;

 public:
  AggOrderExecutor(const AggressiveExecutorConfig& config, IOrderSubsystem* os, GetProductHolderFunc get_product_holder = {})
      : config_(config), os_(os), mea_(MarketExchangeApi::FromString(config.mea())) {
    CHECK_THROW(os_ != nullptr);
    oq_ = os_->GenOrderQuerier();

    fire_sell_ = false;
    fire_buy_ = false;
    best_ask_ = 0.0;
    best_bid_ = 0.0;
    last_buy_ts_ = 0;
    last_sell_ts_ = 0;

    int64_t timestamp = GetCurrentTimestamp();
    auto product = CreateProductFromNormString(mea_, config_.symbol(), timestamp);
    CHECK_THROW(product) << "Symbol not found!\n" << config_.DebugString();

    CHECK(get_product_holder);
    product_holder_.reset(get_product_holder(*product, timestamp)->Clone());
    wo_ = std::make_unique<WorkingOrderState>(product_holder_->product().order_native_symbol(), os);
    cancel_executor_ = std::make_unique<CancelExecutor>(
        wo_.get(), os_->gateway()->need_exchange_id_to_cancel());
  }

  bool ManageAggOrders();
  bool SubmitAggOrders(OrderSide order_side, double order_price, double order_qty,
                       const std::string& tag = "");
  void SetBestPrice(double ask0, double bid0) {
    best_ask_ = ask0;
    best_bid_ = bid0;
  }

  double GetPosition() { return oq_->position(product()); }
  double GetLongPosition() { return oq_->open_long_avail_qty(product()); }
  double GetShortPosition() { return oq_->open_short_avail_qty(product()); }

 private:
  bool CancelRiskOrders();

  void UpdateState() { wo_->UpdateOrderState(); }

  void ResetAction() {
    cancel_executor_->Clear();
    fire_sell_ = false;
    fire_buy_ = false;
  }

  const IProduct& product() { return product_holder_->product(); }
  const IProductInfo& product_info() { return product_holder_->product_info(); }

  void ManageRisk(int64_t timestamp);
  int UpdateBuyOrder(int64_t timestamp, double buy_price, double buy_qty);
  int UpdateSellOrder(int64_t timestamp, double sell_price, double sell_qty);
  bool TryAggBuy(int64_t timestamp, double buy_price, double buy_qty);
  bool TryAggSell(int64_t timestamp, double sell_price, double sell_qty);
  int CheckInput(double price, double qty);
  int64_t LastBuyTimestamp() const { return last_buy_ts_; }
  int64_t LastSellTimestamp() const { return last_sell_ts_; }
  void SubmitOrder(int64_t timestamp, OrderSide order_side, double order_price, double order_qty);
  void SubmitChinaOrder(int64_t timestamp, OrderSide order_side, double order_price,
                        double order_qty);

 private:
  const AggressiveExecutorConfig config_;
  IOrderSubsystem* os_;
  OrderQuerier* oq_;
  const MarketExchangeApi mea_;

  bool fire_sell_;
  bool fire_buy_;
  double best_ask_;
  double best_bid_;
  int64_t last_buy_ts_;
  int64_t last_sell_ts_;

  std::unique_ptr<IProductHolder> product_holder_;
  std::unique_ptr<WorkingOrderState> wo_;
  std::unique_ptr<CancelExecutor> cancel_executor_;
};

}  // namespace coin2::strategy::order_executor
